#include <vector>
#include <algorithm>

#include "BvhNode.h"

using namespace std;

BvhNode::BvhNode(vector<Surface*> objects, int axis) {
    int length = objects.size();

    if (length == 1) {
        left = objects[0];
        right = NULL;
        bbox = objects[0]->boundingBox();
    } else if (length == 2) {
        left = objects[0];
        right = objects[1];
        bbox = combine(objects[0]->boundingBox(), objects[1]->boundingBox());
    } else {
        if (axis == XAXIS) {
            sort(objects.begin(), objects.end(), sortOnX);
        } else if (axis == YAXIS) {
            sort(objects.begin(), objects.end(), sortOnY);
        } else {
            sort(objects.begin(), objects.end(), sortOnZ);
        }

        vector<Surface*> leftList(objects.begin(), objects.begin() + (length / 2));
        vector<Surface*> rightList(objects.begin() + (length / 2), objects.end());

        left = new BvhNode(leftList, (axis + 1) % 3);
        right = new BvhNode(rightList, (axis + 1) % 3);
        bbox = combine(left->boundingBox(), right->boundingBox());
    }
}

bool BvhNode::isHit(Ray& r, double tmin, double tmax, Record& rec) {
    bool leftHit = false, rightHit = false;
    Record lrec;
    Record rrec;

    if (bbox.hitBox(r, tmin, tmax)) {

        if (left != NULL && left->isHit(r, tmin, tmax, lrec)) {
            leftHit = true;
        }

        if (right != NULL && right->isHit(r, tmin, tmax, rrec)) {
            rightHit = true;
        }

        if (leftHit && rightHit) {
            if (lrec.t < rrec.t) {
                rec.t = lrec.t;
                rec.shader = lrec.shader;
                rec.normal = lrec.normal;
            } else {
                rec.t = rrec.t;
                rec.shader = rrec.shader;
                rec.normal = rrec.normal;
            }
            return true;
        } else if (leftHit) {
            rec.t = lrec.t;
            rec.shader = lrec.shader;
            rec.normal = lrec.normal;
            return true;
        } else if (rightHit) {
            rec.t = rrec.t;
            rec.shader = rrec.shader;
            rec.normal = rrec.normal;
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Box BvhNode::boundingBox() {
    return bbox;
}

cs5721::Vector3D BvhNode::getColor() {
    cout << "BVHNODE: GETCOLOR shouldnt be here" << endl;
    return cs5721::Vector3D(0.0, 0.0, 0.0);
}

cs5721::Vector3D BvhNode::getNormal(cs5721::Vector3D&) {
cout << "BVHNODE: GETNORMAL shouldnt be here" << endl;
    return cs5721::Vector3D(0.0, 0.0, 0.0);
}

Shader* BvhNode::getShader() {
    cout << "null shader: shouldnt be here" << endl;
    return NULL;
}

Box BvhNode::combine(Box a, Box b) {
    double minX, maxX, minY, maxY, minZ, maxZ;

    minX = a.minX < b.minX ? a.minX : b.minX;
    maxX = a.maxX > b.maxX ? a.maxX : b.maxX;
    minY = a.minY < b.minY ? a.minY : b.minY;
    maxY = a.maxY > b.maxY ? a.maxY : b.maxY;
    minZ = a.minZ < b.minZ ? a.minZ : b.minZ;
    maxZ = a.maxZ > b.maxZ ? a.maxZ : b.maxZ;

    return Box(minX, maxX, minY, maxY, minZ, maxZ);
}

void BvhNode::print() {
    cout << "BvhNode: ";
    bbox.print();
    if (left != NULL) {
        cout << "Left node: ";
        left->boundingBox().print();
    }
    if (right != NULL) {
        cout << "Right node: ";
        right->boundingBox().print();
    }
    cout << endl << endl;

    if (left != NULL) {
        left->print();
        cout << endl << endl;
    }


    if (right != NULL) {
        right->print();
        cout << endl << endl;
    }

}